package com.jzo2o.orders.manager.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.db.DbRuntimeException;
import cn.hutool.json.JSONUtil;
import co.elastic.clients.json.JsonpUtils;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.jzo2o.api.orders.dto.response.OrderResDTO;
import com.jzo2o.api.orders.dto.response.OrderSimpleResDTO;
import com.jzo2o.common.constants.UserType;
import com.jzo2o.common.enums.EnableStatusEnum;
import com.jzo2o.common.expcetions.CommonException;
import com.jzo2o.common.utils.BeanUtils;
import com.jzo2o.common.utils.CollUtils;
import com.jzo2o.common.utils.JsonUtils;
import com.jzo2o.common.utils.ObjectUtils;
import com.jzo2o.orders.base.config.OrderStateMachine;
import com.jzo2o.orders.base.enums.OrderPayStatusEnum;
import com.jzo2o.orders.base.enums.OrderRefundStatusEnum;
import com.jzo2o.orders.base.enums.OrderStatusEnum;
import com.jzo2o.orders.base.mapper.OrdersMapper;
import com.jzo2o.orders.base.model.domain.Orders;
import com.jzo2o.orders.base.model.domain.OrdersCanceled;
import com.jzo2o.orders.base.model.domain.OrdersRefund;
import com.jzo2o.orders.base.model.dto.OrderSnapshotDTO;
import com.jzo2o.orders.base.model.dto.OrderUpdateStatusDTO;
import com.jzo2o.orders.base.service.IOrdersCommonService;
import com.jzo2o.orders.manager.handler.OrdersHandler;
import com.jzo2o.orders.manager.model.dto.OrderCancelDTO;
import com.jzo2o.orders.manager.model.dto.response.OrdersPayResDTO;
import com.jzo2o.orders.manager.service.IOrdersCanceledService;
import com.jzo2o.orders.manager.service.IOrdersCreateService;
import com.jzo2o.orders.manager.service.IOrdersManagerService;
import com.jzo2o.orders.manager.service.IOrdersRefundService;
import com.jzo2o.redis.helper.CacheHelper;
import lombok.extern.slf4j.Slf4j;
import nonapi.io.github.classgraph.json.JSONUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import static com.jzo2o.orders.base.constants.FieldConstants.SORT_BY;
import static com.jzo2o.orders.base.constants.RedisConstants.RedisKey.ORDERS;
import static com.jzo2o.orders.base.constants.RedisConstants.Ttl.ORDERS_PAGE_TTL;

/**
 * <p>
 * 订单表 服务实现类
 * </p>
 *
 * @author itcast
 * @since 2023-07-10
 */
@Slf4j
@Service
public class OrdersManagerServiceImpl extends ServiceImpl<OrdersMapper, Orders> implements IOrdersManagerService {
    @Resource
    private IOrdersCanceledService ordersCanceledService;
    @Resource
    private OrdersManagerServiceImpl owner;
    @Resource
    private IOrdersCommonService ordersCommonService;
    @Resource
    private IOrdersCreateService ordersCreateService;
    @Resource
    private IOrdersRefundService ordersRefundService;
    @Resource
    private OrdersHandler ordersHandler;
    @Resource
    private OrderStateMachine orderStateMachine;
    @Resource
    private CacheHelper cacheHelper;


    @Override
    public List<Orders> batchQuery(List<Long> ids) {
        LambdaQueryWrapper<Orders> queryWrapper = Wrappers.<Orders>lambdaQuery().in(Orders::getId, ids).ge(Orders::getUserId, 0);
        return baseMapper.selectList(queryWrapper);
    }

    @Override
    public Orders queryById(Long id) {
        return baseMapper.selectById(id);
    }

    /**
     * 滚动分页查询
     *
     * @param currentUserId 当前用户id
     * @param ordersStatus  订单状态，0：待支付，100：派单中，200：待服务，300：服务中，400：待评价，500：订单完成，600：已取消，700：已关闭
     * @param sortBy        排序字段
     * @return 订单列表
     */
    @Override
    public List<OrderSimpleResDTO> consumerQueryList(Long currentUserId, Integer ordersStatus, Long sortBy) {
//        //1.构件查询条件
//        LambdaQueryWrapper<Orders> queryWrapper = Wrappers.<Orders>lambdaQuery()
//                .eq(ObjectUtils.isNotNull(ordersStatus), Orders::getOrdersStatus, ordersStatus)
//                .lt(ObjectUtils.isNotNull(sortBy), Orders::getSortBy, sortBy)
//                .eq(Orders::getUserId, currentUserId)
//                .eq(Orders::getDisplay, EnableStatusEnum.ENABLE.getStatus());
//        Page<Orders> queryPage = new Page<>();
//        queryPage.addOrder(OrderItem.desc(SORT_BY));
//        queryPage.setSearchCount(false);
//
//        //2.查询订单列表
//        Page<Orders> ordersPage = baseMapper.selectPage(queryPage, queryWrapper);
//        List<Orders> records = ordersPage.getRecords();
//        List<OrderSimpleResDTO> orderSimpleResDTOS = BeanUtil.copyToList(records, OrderSimpleResDTO.class);
//        return orderSimpleResDTOS;

        // 6.21 利用索引优化查询订单id列表
        //构件查询条件
        //构件查询条件
        LambdaQueryWrapper<Orders> queryWrapper = Wrappers.<Orders>lambdaQuery()
                .eq(ObjectUtils.isNotNull(ordersStatus), Orders::getOrdersStatus, ordersStatus)
                .lt(ObjectUtils.isNotNull(sortBy), Orders::getSortBy, sortBy)
                .eq(Orders::getUserId, currentUserId)
                .eq(Orders::getDisplay, EnableStatusEnum.ENABLE.getStatus())
                .select(Orders::getId);//只查询id列
        Page<Orders> queryPage = new Page<>();
        queryPage.addOrder(OrderItem.desc(SORT_BY));
        queryPage.setSearchCount(false);

        //查询订单id列表
        Page<Orders> ordersPage = baseMapper.selectPage(queryPage, queryWrapper);
        if (ObjectUtil.isEmpty(ordersPage.getRecords())) {
            return new ArrayList<>();
        }
        //提取订单id列表
        List<Long> ordersIds = CollUtils.getFieldValues(ordersPage.getRecords(), Orders::getId);

        //先查询缓存，缓存没有的再查询数据库 例 查 101 102 缓存有101拿101 102再走数据库
        //利用cacheHelper组件实现这个功能

        //参数1：redisKey的一部分
        String redisKey = String.format(ORDERS, currentUserId);
        //参数2：订单id列表
        //参数3：batchDataQueryExecutor 当缓存中没有时执行batchDataQueryExecutor从数据库查询
        // batchDataQueryExecutor的方法：Map<K, T> execute(List<K> objectIds, Class<T> clazz); objectIds表示缓存中未匹配到的id，clazz指定map中value的数据类型
        //参数4：返回List中的数据类型
        //参数5：过期时间
        List<OrderSimpleResDTO> orderSimpleResDTOS = cacheHelper.<Long, OrderSimpleResDTO>batchGet(redisKey, ordersIds, (noCacheIds, clazz) -> {
            List<Orders> ordersList = batchQuery(noCacheIds);
            if (CollUtils.isEmpty(ordersList)) {
                //为了防止缓存穿透返回空数据
                return new HashMap<>();
            }
            Map<Long, OrderSimpleResDTO> collect = ordersList.stream().collect(Collectors.toMap(Orders::getId, o -> BeanUtil.toBean(o, OrderSimpleResDTO.class)));
            return collect;
        }, OrderSimpleResDTO.class, ORDERS_PAGE_TTL);

        return orderSimpleResDTOS;
    }


//    /**
//     * 根据订单id查询
//     *6.20查询数据库方式
//     * @param id 订单id
//     * @return 订单详情
//     */
//    @Override
//    public OrderResDTO getDetail(Long id) {
//        Orders orders = queryById(id);
//        //开始取消15分钟订单方法而：懒加载方法，打开订单页面判断15分钟订单自动取消
//        orders = canalIfPayOvertime(orders);
//        OrderResDTO orderResDTO = BeanUtil.toBean(orders, OrderResDTO.class);
//        return orderResDTO;
//    }

    /**
     * 根据订单id查询订单详情
     * 方式二 查询订单快照优化
     *
     * @param id 订单id
     * @return
     */
    @Override
    public OrderResDTO getDetail(Long id) {
        //从快照表查询快照
        String currentSnapshotJson = orderStateMachine.getCurrentSnapshotCache(String.valueOf(id));
        OrderSnapshotDTO orderSnapshotDTO = JsonUtils.toBean(currentSnapshotJson, OrderSnapshotDTO.class);
        orderSnapshotDTO = canalIfPayOvertime(orderSnapshotDTO);
        OrderResDTO orderResDTO = BeanUtil.toBean(orderSnapshotDTO, OrderResDTO.class);

        return orderResDTO;
    }
    /**
     * 如果支付过期则取消订单
     * @param orderSnapshotDTO
     */

//    /**
//     * 开始取消15分钟订单方法而：懒加载方法，打开订单页面判断15分钟订单自动取消
//     * @param orders
//     * @return
//     */
//    public Orders canalIfPayOvertime(Orders orders) {
//        //查询订单时判断创建订单未支付15分钟自动取消
//        if (orders.getOrdersStatus() == OrderStatusEnum.NO_PAY.getStatus()
//                && orders.getCreateTime().plusMinutes(15).isBefore(LocalDateTime.now())) {
//            //查询支付结果，如果支付最新状态还是未支付进行取消订单
//            OrdersPayResDTO ordersPayResDTO = ordersCreateService.getPayResultFromTradServer(orders.getId());
//            int payResultFromTradServer = ordersPayResDTO.getPayStatus();
//        if (payResultFromTradServer != OrderPayStatusEnum.PAY_SUCCESS.getStatus()) {
//            //取消订单
//            OrderCancelDTO bean = BeanUtil.toBean(orders, OrderCancelDTO.class);
//
//            bean.setCurrentUserType(UserType.SYSTEM);
//            bean.setCancelReason("订单超时支付，自动取消");
//            cancel(bean);
//            orders = getById(orders.getId());
//        }
//        }
//
//        return orders;
//    }
    /**6.20 优化订单查询使用订单快照
     * 如果支付过期则取消订单：：懒加载方法，打开订单页面判断15分钟订单自动取消
     * @param orderSnapshotDTO
     */
    public OrderSnapshotDTO canalIfPayOvertime(OrderSnapshotDTO orderSnapshotDTO){
        //订单状态
        Integer ordersStatus = orderSnapshotDTO.getOrdersStatus();
        //判断订单是未支付且支付超时(从订单创建时间开始15分钟未支付)
//        if(ordersStatus==OrderStatusEnum.NO_PAY.getStatus() && orders.getCreateTime().isBefore(LocalDateTime.now().minusMinutes(15)) ){
        if(ordersStatus==OrderStatusEnum.NO_PAY.getStatus() && orderSnapshotDTO.getCreateTime().plusMinutes(15).isBefore(LocalDateTime.now())){
            //查询一下最新的支付状态，如果没有支付成功，再执行下边的取消代码
            OrdersPayResDTO payResultFromTradServer = ordersCreateService.getPayResultFromTradServer(orderSnapshotDTO.getId());
            //如果没有支付成功，再执行下边的取消代码
            if(ObjectUtils.isNotNull(payResultFromTradServer) && payResultFromTradServer.getPayStatus()!= OrderPayStatusEnum.PAY_SUCCESS.getStatus()){
                OrderCancelDTO orderCancelDTO = BeanUtils.toBean(orderSnapshotDTO,OrderCancelDTO.class);
                orderCancelDTO.setCurrentUserType(UserType.SYSTEM);
                orderCancelDTO.setCancelReason("订单支付超时系统自动取消");
                cancelByNoPay(orderCancelDTO);

                //从快照中查询订单数据
                String jsonResult = orderStateMachine.getCurrentSnapshotCache(String.valueOf(orderSnapshotDTO.getId()));
                orderSnapshotDTO = JSONUtil.toBean(jsonResult, OrderSnapshotDTO.class);
                return orderSnapshotDTO;
            }

        }
        return orderSnapshotDTO;

    }
    /**
     * 订单评价
     *
     * @param ordersId 订单id
     */
    @Override
    @Transactional
    public void evaluationOrder(Long ordersId) {
//        //查询订单详情
//        Orders orders = queryById(ordersId);
//
//        //构建订单快照
//        OrderSnapshotDTO orderSnapshotDTO = OrderSnapshotDTO.builder()
//                .evaluationTime(LocalDateTime.now())
//                .build();
//
//        //订单状态变更
//        orderStateMachine.changeStatus(orders.getUserId(), orders.getId().toString(), OrderStatusChangeEventEnum.EVALUATE, orderSnapshotDTO);
    }

    /**
     * 取消订单
     * @param orderCancelDTO 取消订单模型
     */
    @Override
    public void cancel(OrderCancelDTO orderCancelDTO) {
        //查询订单信息
        Orders orders = getById(orderCancelDTO.getId());
        BeanUtils.copyProperties(orders,orderCancelDTO);
        if (ObjectUtils.isNull(orders)) {
            throw new DbRuntimeException("找不到要取消的订单{}",orderCancelDTO.getId());
        }
        //订单状态
        Integer ordersStatus = orders.getOrdersStatus();

        //根据订单状态执行取消逻辑
        if(OrderStatusEnum.NO_PAY.getStatus()==ordersStatus){ //订单状态为待支付
            owner.cancelByNoPay(orderCancelDTO);
        }else if(OrderStatusEnum.DISPATCHING.getStatus()==ordersStatus){ //订单状态为派单中
            owner.cancelByDispatching(orderCancelDTO);
            //前面是设置订单为取消，并把记录加入退款表
            //下面要申请退款，定时任务根据退款记录去请求第三方支付服务的退款接口，根据退款结果进行处理，如果退款成功将更新订单的退款状态、删除退款记录
            //新启动一个线程请求退款
            ordersHandler.requestRefundNewThread(orders.getId());
        }else{
            throw new CommonException("当前订单状态不支持取消");
        }
    }

    /**
     * 未支付状态取消订单
     */
    @Transactional(rollbackFor = Exception.class)
    public void cancelByNoPay(OrderCancelDTO orderCancelDTO) {
        //保存取消记录
        //添加订单取消记录，
        OrdersCanceled ordersCanceled = BeanUtil.toBean(orderCancelDTO, OrdersCanceled.class);
        ordersCanceled.setCancellerId(orderCancelDTO.getCurrentUserId());
        ordersCanceled.setCancelerName(orderCancelDTO.getCurrentUserName());
        ordersCanceled.setCancellerType(orderCancelDTO.getCurrentUserType());
        ordersCanceled.setCancelTime(LocalDateTime.now());
        //改变什么表的数据就用什么Service
        ordersCanceledService.save(ordersCanceled);
        //更新订单状态为取消订单
        OrderUpdateStatusDTO orderUpdateStatusDTO = OrderUpdateStatusDTO.builder()
                .id(orderCancelDTO.getId())
                .originStatus(OrderStatusEnum.NO_PAY.getStatus())
                .targetStatus(OrderStatusEnum.CANCELED.getStatus())
                .build();

        int result = ordersCommonService.updateStatus(orderUpdateStatusDTO);
        if (result <= 0) {
            throw new DbRuntimeException("订单取消事件处理失败");
        }
    }

    /**
     * 取消订单中的取消派单状态的订单 取消此类订单需要进行退款
     * 1. 添加取消订单记录。
     * 2. 更新订单状态为“已关闭”。
     * 3. 添加退款记录。
     * //这里只做了1 ， 2步
     * @param orderCancelDTO
     */
    @Transactional(rollbackFor = Exception.class)
    public void cancelByDispatching(OrderCancelDTO orderCancelDTO) {
//保存取消订单记录
        //取消订单记录表
        OrdersCanceled ordersCanceled = BeanUtil.toBean(orderCancelDTO, OrdersCanceled.class);
        ordersCanceled.setCancellerId(orderCancelDTO.getCurrentUserId());
        ordersCanceled.setCancelerName(orderCancelDTO.getCurrentUserName());
        ordersCanceled.setCancellerType(orderCancelDTO.getCurrentUserType());
        ordersCanceled.setCancelTime(LocalDateTime.now());
        ordersCanceledService.save(ordersCanceled);
        //更新订单状态为关闭订单
        OrderUpdateStatusDTO orderUpdateStatusDTO = OrderUpdateStatusDTO.builder().id(orderCancelDTO.getId())
                .originStatus(OrderStatusEnum.DISPATCHING.getStatus())
                .targetStatus(OrderStatusEnum.CLOSED.getStatus())
                .refundStatus(OrderRefundStatusEnum.REFUNDING.getStatus())//退款状态为退款中
                .build();
        int result = ordersCommonService.updateStatus(orderUpdateStatusDTO);
        if (result <= 0) {
            throw new DbRuntimeException("待服务订单关闭事件处理失败");
        }
        //添加退款记录
        OrdersRefund ordersRefund = new OrdersRefund();
        ordersRefund.setId(orderCancelDTO.getId());
        ordersRefund.setTradingOrderNo(orderCancelDTO.getTradingOrderNo());
        ordersRefund.setRealPayAmount(orderCancelDTO.getRealPayAmount());
        ordersRefundService.save(ordersRefund);
    }

}
